import * as Tone from "tone";
import {Midi} from '@tonejs/midi'
import {Frequency, NormalRange, Seconds, Time} from "tone/build/esm/core/type/Units";


export const PIANO_WHITE_KEY_NUM = 52;

export const PIANO_BLACK_KEY_NUM = 36;

// 钢琴谱
export interface PianoSheet {

    // 一个琴谱里有多个谱线，钢琴通常是两条
    tracks: PianoTrack[];

    // 整个乐谱的持续时间
    duration: Seconds;

}

export interface PianoTrack {
    notes: PianoNote[];

    // 单条谱线的持续时间
    duration: Seconds;
}


export interface PianoNote {

    trackId: number;

    time: number;

    duration: number;

    midi: number;

    name: string;

    // 如果设成true, 表示不弹奏该音符
    hide?: boolean;

    // 记录是不是被弹奏了
    attacked?: boolean;
}

export class Piano {

    public static async create(): Promise<Piano> {

        const piano = new Piano();

        await Tone.loaded()
        return piano;
    }

    private samplerNotes = [
        "A0",
        "C1", "D#1", "F#1", "A1",
        "C2", "D#2", "F#2", "A2",
        "C3", "D#3", "F#3", "A3",
        "C4", "D#4", "F#4", "A4",
        "C5", "D#5", "F#5", "A5",
        "C6", "D#6", "F#6", "A6",
        "C7", "D#7", "F#7", "A7",
        "C8"
    ]

    private sampler: Tone.Sampler;

    public constructor() {
        // 我们目前使用力度是8的音源
        const velocity = "v8";

        const samplesMap: Record<string, string> = {}
        for (const note of this.samplerNotes) {
            samplesMap[note] = `${note.replace("#", "s")}${velocity}.ogg`;
        }

        this.sampler = new Tone.Sampler({
            urls: samplesMap,
            release: 1,
            baseUrl: "/audio/",
        }).toDestination();
    }

    public triggerAttackRelease(notes: Frequency[] | Frequency, duration: Time | Time[], time?: Time, velocity?: NormalRange): void {

        this.sampler.triggerAttackRelease(notes, duration, time, velocity);
    }

    public triggerAttack(notes: Frequency | Frequency[], time?: Time, velocity?: NormalRange): void {
        this.sampler.triggerAttack(notes, time, velocity);
    }


    public triggerRelease(notes: Frequency | Frequency[], time?: Time): void {
        this.sampler.triggerRelease(notes, time);
    }

    /**
     *
     * @param pianoSheet
     * @param baseTime 基准时间，由于midi中以0秒作为开始，但是Tone.js上下文可能在很早以前就创建了，因此当调用该方法播放的时候，时间基准很可能不是0
     */
    public playPianoSheet(pianoSheet: PianoSheet, baseTime?: number): void {

        if (!baseTime) {
            baseTime = this.sampler.now();
        }

        pianoSheet.tracks.forEach(track => {
            track.notes.filter(each => !each.hide).forEach(note => {
                this.triggerAttackRelease(note.name, note.duration, baseTime + note.time);
            })
        });
    }
}

export async function loadPianoSheet(pianoSheetName: string): Promise<PianoSheet> {

    // load a midi file in the browser
    const midi = await Midi.fromUrl(`/sheet/${pianoSheetName}.mid`);

    const pianoSheet: PianoSheet = {
        tracks: midi.tracks.map((track, index) => ({
            notes: track.notes.map(note => ({
                time: note.time,
                name: note.name,
                duration: note.duration,
                midi: note.midi,
                trackId: index
            })),
            duration: track.duration
        })),
        duration: midi.duration
    }

    console.log(midi)

    return pianoSheet;
}

//
// export async function loadPianoSheet2(pianoSheetName: string): Promise<PianoSheet> {
//
//     await sleep(2000);
//
//     return {
//         "tracks": [
//             {
//                 "notes": [
//                     {
//                         "trackId": 0,
//                         "time": 0.5,
//                         "name": "C3",
//                         "duration": 1,
//                         "midi": 22
//                     },
//                     {
//                         "trackId": 0,
//                         "time": 1.5,
//                         "name": "D3",
//                         "duration": 1,
//                         "midi": 22
//                     },
//                     {
//                         "trackId": 0,
//                         "time": 2.5,
//                         "name": "E3",
//                         "duration": 1,
//                         "midi": 22
//                     },
//                     {
//                         "trackId": 0,
//                         "time": 3.5,
//                         "name": "F3",
//                         "duration": 1,
//                         "midi": 22
//                     },
//                     {
//                         "trackId": 0,
//                         "time": 4.5,
//                         "name": "G3",
//                         "duration": 1,
//                         "midi": 22
//                     },
//                     {
//                         "trackId": 0,
//                         "time": 5.5,
//                         "name": "A3",
//                         "duration": 1,
//                         "midi": 22
//                     },
//                     {
//                         "trackId": 0,
//                         "time": 6.5,
//                         "name": "B3",
//                         "duration": 1,
//                         "midi": 22
//                     },
//                 ],
//                 duration: 7
//             }
//         ],
//         duration: 7
//     };
// }
//
// async function sleep(ms: number) {
//     return new Promise(resolve => setTimeout(resolve, ms));
// }